home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / asmutil / asm_n_z.zip / SWAP.ASM < prev    next >
Assembly Source File  |  1989-08-09  |  86KB  |  2,079 lines

  1. page    60,132
  2. ;Preamble:
  3. ;       Received this file from Raymond Michels, author of "Undocumented
  4. ;DOS Internals," (1989 Programmers' Journal 7.2, pp. 32-37).
  5. ;       Although it makes no mention, it clearly builds on TSRDEMO2.ASM
  6. ;that is authored by Thomas Brandenborg (Lundbyesgade 11, DK-8000
  7. ;Aarhus C., DENMARK)  which is shareware found on Channel 1 BBS (617-354-
  8. ;8873, Boston)
  9. ;       There are a few notable differences between the two:
  10. ;       1.  DEMO only went so far as to popup a message while this program
  11. ;       will free 192K of memory used by the current application and then run
  12. ;       another program in that space (NOTE: this program should not be;
  13. ;       confused with SWAP being distributed as SWAPIT.ZIP by Nico Mak,
  14. ;       Mansfield Software Group, Storrs,CT and described in Dr. Dobbs
  15. ;       April, 1989)
  16. ;       2.  SWAP handles screen storage and restoration
  17. ;       3.  SWAP adds scrolldown and scrollup in SCAN Codes
  18. ;       4.  SWAP adds an sti (opcode) in OurInt13 just after the
  19. ;             call    OldInt13
  20. ;       5.  SWAP adds a deinstall TSR routine
  21. ;       6.  SWAP modifies handling of ^C with addition of PrevInt23 variable
  22. ;       7.  SWAP changes messages in general
  23. ;       8.  SWAP adds routine CLS to clear screen
  24. ;       9.  SWAP adds saving of screen mode (@ IsColor:)
  25. ;      10.  SWAP removed treatment of EnvSeg at very end
  26. ;
  27. ;              W. Curtiss Priest, Humanic Systems
  28. ;               Lexington, MA, 02173
  29. ;
  30.  
  31. ;               CONTEXT SWITCH
  32.  
  33.  
  34. ;==============================================================================
  35. ; DEFINE BIOS DATA SEGMENT OFFSETS
  36. ;==============================================================================
  37.  
  38. BiosData        segment at 40h
  39.                 org     17h
  40. KbFlag          label   byte            ;current shift status bits
  41.                 org     18h
  42. KbFlag1         label   byte            ;current key status of toggle keys
  43. BiosData        ends
  44.  
  45. ;==============================================================================
  46. ; DEFINE OFFSETS WITHIN BIOS EXTRA DATA SEG
  47. ;==============================================================================
  48.  
  49. BiosXX          segment at 50h
  50.                 org     0
  51. StatusByte      label   byte            ;PrtSc status
  52. BiosXX          ends
  53.  
  54. ErrPrtSc        equ     -1              ;err during last PrtSc
  55. InPrtSc         equ     1               ;PrtSc in progress
  56.  
  57. ;==============================================================================
  58. ; DEFINE OFFSETS WITHIN OUR PSP
  59. ;==============================================================================
  60.  
  61. Cseg            segment byte public
  62.                 org     2
  63. TopSeg          label   word            ;last seg in alloc block
  64.                 org     2ch
  65. EnvSeg          label   word            ;seg of our environment copy
  66. Cseg            ends
  67.  
  68. ;==============================================================================
  69. ; DOS COM-FILE ENTRY POINT
  70. ;==============================================================================
  71.  
  72. Cseg            segment public byte
  73.                 assume  cs:Cseg, ds:nothing, es:nothing, ss:nothing
  74.                 org     100h
  75.  
  76.  
  77. ComEntry:       jmp     Init            ;JMP to init at bottom of seg
  78.  
  79.  
  80.  
  81.  
  82. ;==============================================================================
  83. ; IDENTIFICATION CODES FOR THIS TSR (MUST BE UNIQUE FOR EACH CO-EXISTING TSR)
  84. ; HIGH BYTE OF GetId MUST NOT MATCH ANY AH REQUEST CODES FOR INT16H.
  85. ;==============================================================================
  86.  
  87. GetId           equ     "bn"                    ;INT16h AX val to get MyId
  88. MyId            equ     "BN"                    ;ID of this TSR
  89.  
  90. ;==============================================================================
  91. ; FLAGS AND PTRS FOR RESIDENT HANDLING
  92. ;==============================================================================
  93.  
  94. TsrMode         db      0                       ;bits for various modes
  95. InInt08         equ     1 SHL 0                 ;timer0 tick handler
  96. InInt09         equ     1 SHL 1                 ;keyboard handler
  97. InInt13         equ     1 SHL 2                 ;BIOS disk I/O
  98. InInt28         equ     1 SHL 3                 ;INT28 handler
  99. In28Call        equ     1 SHL 4                 ;we have issued INT28
  100. InPopup         equ     1 SHL 5                 ;popup routine activated
  101. NewDos          equ     1 SHL 6                 ;DOS 2.x in use
  102. InDosClr        equ     1 SHL 7                 ;InDos 0 at popup time
  103.  
  104. KeyMode         db      0                       ;bits for hotkey status
  105. HotIsShift      equ     1 SHL 0                 ;hotkey is shift state
  106. InHotMatch      equ     1 SHL 1                 ;so far keys match hotkey seq
  107. HotKeyOn        equ     1 SHL 2                 ;full hotkey pressed
  108.  
  109. InDosPtr        label   dword                   ;seg:off of InDos flag
  110. InDosOff        dw      0
  111. InDosSeg        dw      0
  112.  
  113. CritErrPtr      label   dword                   ;seg:off of CritErr flag
  114. CritErrOff      dw      0
  115. CritErrSeg      dw      0
  116.  
  117. GetOutFlag      dw      0                       ;set to cause de-install
  118.  
  119. ;==============================================================================
  120. ; DATA FOR INT09H HANDLER TO CHECK FOR HOTKEY COMBINATION
  121. ;==============================================================================
  122.  
  123. ; ------------  EQU'S FOR BIT SHIFTS WITHIN KEYBOARD FLAGS
  124.  
  125. InsState        equ     80h
  126. CapsState       equ     40h
  127. NumState        equ     20h
  128. ScrollState     equ     10h
  129. AltShift        equ     08h
  130. CtlShift        equ     04h
  131. LeftShift       equ     02h
  132. RightShift      equ     01h
  133.  
  134. InsShift        equ     80h
  135. CapsShift       equ     40h
  136. NumShift        equ     20h
  137. ScrollShift     equ     10h
  138. HoldState       equ     08h
  139.  
  140. ; ------------  SCAN CODES FOR VARIOUS SHIFT KEYS
  141.  
  142. LeftDown        equ     42                      ;scan code of left shift key
  143. LeftUp          equ     LeftDown OR 80h
  144. RightDown       equ     54                      ;scan code of right shift key
  145. RightUp         equ     RightDown OR 80h
  146. AltDown         equ     56                      ;scan code of alt key
  147. AltUp           equ     AltDown OR 80h
  148. CtlDown         equ     29                      ;scan code of ctrl key
  149. CtlUp           equ     CtlDown OR 80h
  150. ScrollDown      equ     70                      ;scan code of scroll lock
  151. ScrollUp        equ     ScrollDown OR 80h
  152.  
  153. ; ------------  MISC KEYBOARD DATA
  154.  
  155. KbData          equ     60h                     ;keyboard data input
  156.  
  157. ;==============================================================================
  158. ; TO USE A SHIFT KEY COMBINATION AS HOT KEY:
  159. ;  -    SET THE FLAG HotIsShift IN KeyMode
  160. ;  -    DEFINE THE SHIFT STATUS BITS IN THE VARIABLE HotKeyShift
  161. ;
  162. ; TO USE A SERIES OF SCAN CODES AS HOT KEY:
  163. ;  -    CLEAR THE FLAG HotIsShift IN KeyMode
  164. ;  -    INSERT THE MAKE AND BREAK SCAN CODES IN THE HotKeySeq STRING
  165.  
  166. ;       NOTE:   WITH THIS IMPLEMENTATION YOU SHOULD NOT USE A HOT KEY
  167. ;               SEQUENCE WHICH PRODUCES A KEY IN THE BIOS KEYBOARD QUEUE,
  168. ;               SINCE THE KEY IS NOT REMOVED BEFORE CALLING THE POPUP ROUTINE.
  169. ;
  170. ; NOTE: HOTKEY TYPE AND CONTENTS OF HOTKEY VARIABLES MAY BE CHANGED AT RUN TIME
  171. ;==============================================================================
  172.  
  173. HotKeyShift     db      AltShift OR RightShift  ;shift state IF HotIsShift=FF
  174.  
  175. HotKeySeq       db      AltDown,RightDown
  176. HotKeyLen       equ     $-HotKeySeq
  177.  
  178. HotIndex        db      0                       ;# key in seq to compare next
  179. BetweenKeys     db      0                       ;timeout count between keys
  180. KeyTimeOut      equ     10                      ;more ticks means not a hotkey
  181.  
  182. ;==============================================================================
  183. ; DATA FOR INT08H HANDLER TO CHECK FOR POPUP
  184. ;==============================================================================
  185.  
  186. SafeWait        db      0                       ;count-down for safe popup
  187. MaxWait         equ     8                       ;wait no more 8/18 sec
  188.  
  189. ;==============================================================================
  190. ; PROCESS & SYSTEM DATA
  191. ;==============================================================================
  192.  
  193. OurSS           dw      0                       ;stack for popup routine
  194. OurSP           dw      0
  195. StackSize       equ     512                     ;bytes to reserve for stack
  196.  
  197. OldSS           dw      0                       ;old stack seg
  198. OldSP           dw      0                       ;old stack off
  199.  
  200. OurPSP          dw      0                       ;our PSP seg
  201. OldPSP          dw      0                       ;old PSP seg
  202.  
  203. OldDTA          label   dword                   ;seg:off of old DTA area
  204. OldDTAOff       dw      0
  205. OldDTASeg       dw      0
  206.  
  207. OurDTA          label   dword                   ;seg:off of our DTA
  208. OurDTAOff       dw      0
  209. OurDTASeg       dw      0
  210.  
  211. OldBreak        db      0                       ;old ctrl-break state
  212. OldExtErr       dw      3 dup (0)               ;AX,BX,CX of ext err
  213.  
  214. ;==============================================================================
  215. ; LOCATIONS FOR SAVED INTERRUPT VECTORS
  216. ;==============================================================================
  217.  
  218. OldInt08        label   dword                   ;Timer0 loaded before this
  219. OldInt08Off     dw      0
  220. OldInt08Seg     dw      0
  221.  
  222. OldInt09        label   dword                   ;Kb handler loadde before this
  223. OldInt09Off     dw      0
  224. OldInt09Seg     dw      0
  225.  
  226. OldInt13        label   dword                   ;BIOS diskette I/O
  227. OldInt13Off     dw      0
  228. OldInt13Seg     dw      0
  229.  
  230. OldInt16        label   dword                   ;BIOS kb Q-handler
  231. OldInt16Off     dw      0
  232. OldInt16Seg     dw      0
  233.  
  234. OldInt1B        label   dword                   ;^break of process we steal
  235. OldInt1BOff     dw      0
  236. OldInt1BSeg     dw      0
  237.  
  238. OldInt1C        label   dword                   ;timer tick of process we steal
  239. OldInt1COff     dw      0
  240. OldInt1CSeg     dw      0
  241.  
  242. OldInt21        label   dword                   ;DOS function dispatcher
  243. OldInt21Off     dw      0
  244. OldInt21Seg     dw      0
  245.  
  246. PrevInt23       label   dword                   ;^C of process we steal
  247. PrevInt23Off    dw      0
  248. PrevInt23Seg    dw      0
  249.  
  250. OldInt23        label   dword                   ;^C internal handler (IRET)
  251. OldInt23Off     dw      0
  252. OldInt23Seg     dw      0
  253.  
  254. OldInt24        label   dword                   ;crit err of process we steal
  255. OldInt24Off     dw      0
  256. OldInt24Seg     dw      0
  257.  
  258. OldInt28        label   dword                   ;DOS idles loaded before this
  259. OldInt28Off     dw      0
  260. OldInt28Seg     dw      0
  261.  
  262. ApplPSP         dw      0                       ;stores application PSP
  263. CurrentAlloc    dw      0                       ;application memory allocation
  264.  
  265. ;==============================================================================
  266. ; SPEAKER/TONE GENERATION DATA
  267. ;==============================================================================
  268.  
  269. PB0port         equ     61h                     ;port for speaker bit
  270. ErrLen1         equ     10                      ;# outer err beep cycles
  271. ErrLen2         equ     80                      ;# inner err beep cycles
  272. ErrLow          equ     100                     ;low tone wait in err beep
  273. ErrHi           equ     40                      ;hi tone wait in err beep
  274.  
  275. ;==============================================================================
  276. ; ErrBeep - PRODUCE ERROR-INDICATING SOUND ON SPEAKER
  277. ;==============================================================================
  278.  
  279. ErrBeep         proc    near
  280.                 assume  ds:nothing, es:nothing, ss:nothing
  281.  
  282.                 push    ax                      ;save regs used
  283.                 push    bx
  284.                 push    cx
  285.                 push    dx
  286.  
  287.                 mov     cx,ErrLen1              ;# mix-cycles for beep
  288.  
  289. ErrBeep1:       mov     dx,ErrLow               ;wait time for half-cycle
  290.                 mov     bx,ErrLen2              ;len of one tone
  291.                 call    DoTone                  ;output low err tone
  292.                 mov     dx,ErrHi                ;wait time for half-cycle
  293.                 mov     bx,ErrLen2              ;len of one tone
  294.                 call    DoTone                  ;output low err tone
  295.  
  296.                 loop    ErrBeep1                ;loop for some time
  297.  
  298.                 pop     dx
  299.                 pop     cx                      ;restore regs
  300.                 pop     bx
  301.                 pop     ax
  302.                 ret
  303. ErrBeep         endp
  304.  
  305. ;==============================================================================
  306. ; DoTone - OUTPUT ONE TONE ON THE SPEAKER
  307. ;
  308. ; INPUT:        DX:     LOOP WAIT TIME FOR HALF CYCLE IN TONE
  309. ;               BX:     NUMBER OF CYCLES FOR TONE DURATION
  310. ; OUTPUT:       NONE
  311. ; REGS:         ALL PRESERVED
  312. ;==============================================================================
  313.  
  314. DoTone          proc    near
  315.                 assume  ds:nothing, es:nothing, ss:nothing
  316.  
  317.                 push    ax                      ;save regs used
  318.                 push    bx
  319.                 push    cx
  320.                 in      al,PB0port              ;get PB0 reg pattern
  321.                 mov     ah,al                   ;save it
  322.  
  323. DoTone1:        and     al,0fch                 ;mask off speaker bit
  324.                 out     PB0port,al              ;pull!
  325.                 mov     cx,dx                   ;half cycle in counter
  326. DoTone2:        loop    DoTone2                 ;leave there for half a cycle
  327.                 or      al,2                    ;turn on speaker bit
  328.                 out     PB0port,al              ;push!
  329.                 mov     cx,dx                   ;half cycle in counter
  330. DoTone3:        loop    DoTone3                 ;leave there for half a cycle
  331.  
  332.                 dec     bx                      ;count down tone duration
  333.                 jnz     DoTone1                 ;go through full tone
  334.  
  335.                 mov     al,ah                   ;AL=original PB0 reg value
  336.                 out     PB0port,al              ;restore
  337.  
  338.                 pop     cx                      ;restore regs
  339.                 pop     bx
  340.                 pop     ax
  341.                 ret
  342. DoTone          endp
  343.  
  344. ;==============================================================================
  345. ; TestSafe - CHECK IF THIS IS A SAFE TIME TO DO A POP UP
  346. ; RETURN CLC IF SAFE TO POP UP, CY IF NOT SAFE.
  347. ;
  348. ; CHECK IF ANY INTs ARE IN CRITICAL AREAS (InInt09 & InInt13)
  349. ; CHECK IF WE ARE IN AN OUR OWN INT28 CALL (In28Call)
  350. ; CHECK 8259A PIC ISR REGISTER FOR MISSING EOIs
  351. ; CHECK IF DOS IS STABLE FOR POP UP
  352. ; CHECK IF A PRINT SCREEN IS IN PROGRESS
  353. ;==============================================================================
  354.  
  355. TestSafe        proc    near
  356.                 assume  ds:nothing, es:nothing
  357.  
  358.                 push    ax                      ;save regs used
  359.                 push    bx
  360.                 push    ds
  361.  
  362. ; ------------  CHECK INTs TO SEE IF THEY WERE INTERRUPTED AT BAD TIMES
  363.  
  364.                 test    TsrMode,InInt09 OR InInt13 OR In28Call
  365.                 jnz     NotSafe                 ;jump if any INTs are chopped
  366.  
  367. ; ------------  CHECK THE 8259A PIC ISR REGISTER FOR NON-EOIed HW INTs
  368.  
  369.                 mov     al,00001011b            ;tell 8259A we want the ISR
  370.                 out     20h,al                  ;8259A command reg
  371.                 nop
  372.                 nop
  373.                 nop                             ;now, ISR should be ready
  374.                 in      al,20h                  ;AL=mask of active INTs
  375.                 or      al,al                   ;test all (IRQ0 *did* EOI)
  376.                 jnz     NotSafe                 ;jump if active INTs
  377.  
  378. ; ------------  NOW, ENSURE THAT DOS WAS NOT INTERRUPTED
  379.  
  380.                 assume  ds:nothing
  381.  
  382.                 lds     bx,InDosPtr             ;now, DS:BX=InDos
  383.                 mov     al,byte ptr [bx]        ;get InDos to AL
  384.                 lds     bx,CritErrPtr           ;now, DS:BX=CritErr
  385.                 or      al,byte ptr [bx]        ;both flags zero?
  386.                 jz      DosSafe                 ;YES - DOS is really idle
  387.                 test    TsrMode,InInt28         ;is this an INT28h
  388.                 jz      NotSafe                 ;NO - not safe, should be idle
  389.                 cmp     al,1                    ;YES - one InDos entry only?
  390.                 ja      NotSafe                 ;NO - jump if more than one
  391. DosSafe:
  392.  
  393. ; ------------  CHECK TO SEE IF A PRINT SCREEN IS IN PROGRESS
  394.  
  395.                 mov     ax,BiosXX
  396.                 mov     ds,ax                   ;move DS to BIOS extra data seg
  397.                 assume  ds:BiosXX
  398.  
  399.                 cmp     StatusByte,InPrtSc      ;print screen in progress?
  400.                 je      NotSafe                 ;YES - jump if prtsc
  401.  
  402. ; ------------  SEEMS TO BE A SAFE TIME FOR POPUP
  403.  
  404. IsSafe:         clc                             ;CLC=safe to popup
  405.                 jmp     short ExitSafe          ;end this then
  406.  
  407. ; ------------  APPARENTLY THIS IS JUST NOT THE TIME TO DO A POPUP
  408.  
  409. NotSafe:        stc                             ;CY=don't popup now
  410.  
  411. ; ------------  RETURN TO CALLER WITH CARRY SET/CLEAR
  412.  
  413. ExitSafe:       pop     ds                      ;restore regs
  414.                 pop     bx
  415.                 pop     ax
  416.                 ret
  417. TestSafe        endp
  418.  
  419. ;==============================================================================
  420. ; OurInt08 - TSR INT08H HANDLER TO WATCH FOR HOTKEY AND SAFE POPUP TIMES
  421. ;
  422. ; CALL OldInt08
  423. ; CHECK FOR RE-ENTRANCE INTO CRITICAL INT08 CODE
  424. ; SET InInt08 FLAG
  425. ; CHECK FOR TIMEOUT BETWEEN KEYS IN HOTKEY SEQUENCE
  426. ; CHECK IF HOTKEY WAS PRESSED
  427. ; CHECK IF ALREADY InPopup OR InInt28
  428. ; CHECK IF SAFE TIME FOR SYSTEM TO POPUP
  429. ; UPDATE FLAGS AND CALL POPUP IF SAFE
  430. ; GIVE ERROR BEEP IF POPUP WAS UNSAFE FOR A LONG TIME
  431. ; RESET InInt08 FLAG
  432. ; DO IRET
  433. ;==============================================================================
  434.  
  435. ; ------------  NEAR JUMP DESTINATION FOR FAST IRET'S
  436.  
  437. Exit08:         iret                            ;IRET (!)
  438.  
  439. ; ------------  ACTUAL INT08 ENTRY POINT
  440.  
  441. OurInt08        proc    far
  442.                 assume  ds:nothing, es:nothing, ss:nothing
  443.  
  444.                 pushf                           ;simulate INT08
  445.                 cli                             ;in case others forgot it
  446.                 call    OldInt08                ;call TSRs loaded before us
  447.  
  448. ; ------------  ENSURE NO RECURSION INTO CRITICAL INT08 CODE
  449.  
  450.                 sti                             ;we'll manage INTs
  451.  
  452.                 test    TsrMode,InInt08         ;already in here somewhere?
  453.                 jnz     Exit08                  ;YES - don't re-enter
  454.                 or      TsrMode,InInt08         ;tell people we are here
  455.  
  456.                 push    ax                      ;need a few regs in this code
  457.  
  458. ; ------------  COUNT DOWN TIME-OUT BETWEEN KEYS IN HOTKEY SEQUENCE
  459.  
  460.                 test    KeyMode,InHotMatch      ;are we in a key match?
  461.                 jz      TestHot08               ;NO - don't care then
  462.                 dec     BetweenKeys             ;count down timeout val
  463.                 jnz     TestHot08               ;jump if no timeout yet
  464.                 mov     HotIndex,0              ;start match from beginning
  465.                 and     KeyMode,not InHotMatch  ;just so we know it next time
  466.  
  467. ; ------------  CHECK FOR POSSIBLE POPUP ACTIONS
  468.  
  469. TestHot08:      test    KeyMode,HotKeyOn        ;has hotkey been pressed?
  470.                 jz      ExitInt08               ;NO - jump if no fun here
  471.  
  472.                 test    TsrMode,InInt28 OR InPopup
  473.                 jnz     ExitInt08               ;jmp if not alr in business
  474.  
  475. ; ------------  HOTKEY PRESSED, CHECK TO SEE IF IT IS SAFE TO POPUP
  476.  
  477.                 cmp     SafeWait,0              ;first time we find hotkey?
  478.                 ja      TestSafe08              ;NO - wait has alr been set
  479.                 mov     SafeWait,MaxWait        ;# ticks to wait at most
  480.  
  481. TestSafe08:     call    TestSafe                ;now, CY clear if popup is safe
  482.                 jc      NotSafe08               ;jump if popup is bad idea
  483.  
  484. ; ------------  SEEMS SAFE TO POPUP AT THIS TIME, SO DO!
  485.  
  486.                 xor     al,al                   ;fast zero
  487.                 mov     SafeWait,al             ;don't count any more
  488.                 and     KeyMode,not HotKeyOn    ;clear hotkey status
  489.                 or      TsrMode,InPopup         ;tell'em we enter popup routine
  490.                 and     TsrMode,not InInt08     ;OK to enter critical INT08
  491.                 call    InitPopup               ;do actual popup
  492.                 or      TsrMode,InInt08         ;back in INT08 code here
  493.                 and     TsrMode,not InPopup     ;not in popup code any more
  494.                 mov     SafeWait,al             ;in case of hotkey during popup
  495.                 and     KeyMode,not HotKeyOn    ;clear hotkey status
  496.  
  497.                 jmp     short ExitInt08         ;finally done
  498.  
  499. ; ------------  UNSAFE POPUP TIME, COUNT DOWN SafeWait
  500.  
  501. NotSafe08:      dec     SafeWait                ;count down waiter
  502.                 jnz     ExitInt08               ;jump if still no timeout
  503.  
  504. ; ------------  NO SAFE TIMES FOUND FOR QUITE SOME TIME, ERROR
  505.  
  506.                 and     KeyMode,not HotKeyOn    ;might as well clear hotkey
  507.                 call    ErrBeep                 ;do an error beep
  508.  
  509. ; ------------  NORMAL INT08H EXIT, RESET InInt08
  510.  
  511. ExitInt08:      pop     ax                      ;restore regs used
  512.                 and     TsrMode,not InInt08     ;clear that flag
  513.                 iret                            ;straight back
  514. OurInt08        endp
  515.  
  516. ;==============================================================================
  517. ; OurInt09 - TSR INT09H HANDLER TO WATCH FOR HOTKEY
  518. ;
  519. ; SAVE SCAN CODE
  520. ; CALL OldInt09
  521. ; CHECK FOR RECURSION INTO CRITICAL INT09 CODE
  522. ; SET InInt09 FLAG
  523. ; CHECK IF HOTKEY ALREADY SET
  524. ; DETERMINE HOTKEY TYPE (SHIFT STATE OR KEY SEQENCE)
  525. ; CHECK SHIFT STATE IF HotIsShift
  526. ; COMPARE FOR KEY MATCH IF (NOT HotIsShift)
  527. ; SET HotKeyOn IF HOTKEY PRESSED
  528. ; RESET InInt09 FLAG
  529. ; DO IRET
  530. ;==============================================================================
  531.  
  532. ; ------------  NEAR JUMP DESTINATION FOR EARLY EXITS
  533.  
  534. Exit09:         pop     bx                      ;restore regs
  535.                 pop     ax
  536.                 iret                            ;flags restored from stack
  537.  
  538. ; ------------  ACTUAL INT09 ENTRY POINT
  539.  
  540. OurInt09        proc    far
  541.                 assume  ds:nothing, es:nothing, ss:nothing
  542.  
  543.                 push    ax                      ;save regs used
  544.                 push    bx
  545.  
  546. ; ------------  READ SCAN CODE, IN CASE SEQUENCE MATCHING SELECTED
  547.  
  548.                 in      al,KbData               ;Al=key, preserved by BIOS
  549.  
  550. ; ------------  CALL BIOS TO PERFORM ITS DUTIES
  551.  
  552.                 pushf                           ;simulate INT (CLI alr set)
  553.                 cli                             ;in case others forgot it
  554.                 call    OldInt09                ;call BIOS/earlier TSRs
  555.  
  556. ; ------------  ENSURE NO RECURSION INTO CRITICAL INT09 CODE
  557.  
  558.                 sti                             ;we'll manage INTs
  559.  
  560.                 test    TsrMode,InInt09         ;alr in business?
  561.                 jnz     Exit09                  ;YES - skip test till clear
  562.                 or      TsrMode,InInt09         ;tell them we arrived here
  563.  
  564. ; ------------  DETERMINE HOT KEY TYPE SELECTED
  565.  
  566.                 test    KeyMode,HotKeyOn        ;already hotkey there?
  567.                 jnz     ExitInt09               ;YES - no double hotkeys here
  568.  
  569.                 test    KeyMode,HotIsShift      ;shift state type hotkey?
  570.                 jz      CompSeq09               ;NO - go compare sequence
  571.  
  572. ; ------------  COMPARE CURRENT SHIFT STATUS AGAINST HOTKEY
  573.  
  574.                 push    ds                      ;save current ds
  575.                 mov     ax,BiosData             ;move DS to BIOS data seg
  576.                 mov     ds,ax                   ;DS can now access keyb vars
  577.                 assume  ds:BiosData             ;tell MASM about our DS
  578.                 mov     al,KbFlag               ;get BIOS shift state bits
  579.                 pop     ds                      ;restore
  580.                 assume  ds:nothing              ;last thing we know about him
  581.  
  582.                 and     al,HotKeyShift          ;isolate relevant bits
  583.                 cmp     al,HotKeyShift          ;our shift state in effect?
  584.                 jne     ExitInt09               ;NO - not that shift state
  585.                 or      KeyMode,HotKeyOn        ;YES - flag hotkey
  586.                 jmp     short ExitInt09         ;now we can be proud to leave
  587.  
  588. ; ------------  MATCH KEY IN SCAN CODE SEQUENCE
  589.  
  590. CompSeq09:      mov     bl,HotIndex             ;next scan code to match
  591.                 xor     bh,bh                   ;must be word
  592.                 cmp     al,HotKeySeq[bx]        ;does key match?
  593.                 je      HotMatch09              ;YES - jump if match
  594.                 mov     HotIndex,bh             ;search from start next time
  595.                 and     KeyMode,not InHotMatch  ;current no match
  596.                 jmp     short ExitInt09         ;now end this
  597.  
  598. ; ------------  KEY MATCHED...NEXT SCAN CODE IN HotKeySeq
  599.  
  600. HotMatch09:     inc     bl                      ;new code at next pass
  601.                 cmp     bl,HotKeyLen            ;did we match whole sequence?
  602.                 jae     HotHit09                ;YES - jump if full sequence
  603.                 mov     HotIndex,bl             ;NO - save new count
  604.                 mov     BetweenKeys,KeyTimeOut  ;reset counter between keys
  605.                 or      KeyMode,InHotMatch      ;we are in a match now
  606.                 jmp     short ExitInt09         ;time to end this
  607.  
  608. ; ------------  KEY MATCHED ALL SCAN CODES IN HOTKEY SEQUENCE
  609.  
  610. HotHit09:       or      KeyMode,HotKeyOn        ;say hotkey was pressed
  611.                 mov     HotIndex,bh             ;match 1st code next time
  612.                 and     KeyMode,not InHotMatch  ;that's the end of a match
  613.  
  614. ; ------------  EXIT FROM INT09H, RESET InInt09 FLAG
  615.  
  616. ExitInt09:      and     TsrMode,not InInt09     ;tell'em we left this code
  617.                 pop     bx                      ;restore regs
  618.                 pop     ax
  619.                 iret                            ;flags restored from stack
  620. OurInt09        endp
  621.  
  622. ;==============================================================================
  623. ; OurInt13 - SET InInt13 FLAG TO SAY THAT WE ARE IN AN INT13H
  624. ;==============================================================================
  625.  
  626. OurInt13        proc    far
  627.                 assume  ds:nothing, es:nothing, ss:nothing
  628.  
  629.                 pushf                           ;save flags we use
  630.                 or      TsrMode,InInt13         ;remember we are in BIOS now
  631.                 popf                            ;restore flags
  632.  
  633.                 pushf                           ;simulate INT13
  634.                 cli                             ;just in case others forgot
  635.                 call    OldInt13                ;let BIOS handle it all
  636.                 sti                             ;turn 'em back on
  637.  
  638.                 pushf                           ;BIOS uses flag return
  639.                 and     TsrMode, not InInt13    ;tell people we left INT13h
  640.                 popf
  641.  
  642.                 ret     2                       ;throw flags off stack
  643. OurInt13        endp
  644.  
  645. ;==============================================================================
  646. ; OurInt16 - TSR INT16H HANDLER, INT28 CHAIN INTERFACE
  647. ;
  648. ; INPUT:        AX = GetId
  649. ; OUTPUT:       AX = MyId
  650. ; REGS:         AX LOST, ALL OTHERS PRESERVED
  651. ; DESCRIPTION:  DETERMINE IF TSR WITH THIS ID IS ALREADY IN MEMORY
  652. ;
  653. ; INPUT:        AH = 00
  654. ; OUTPUT:       AX = NEXT KEY FROM BUFFER
  655. ; REGS;         AX LOST, ALL OTHERS PRESERVED
  656. ; DESCRIPTION:  RETURN A KEY FROM KEYBOARD BUFFER, WAIT TILL KEY IS PRESSED
  657. ;
  658. ; INPUT:        AH = 02
  659. ; OUTPUT:       AX = KEY FROM BUFFER IN ANY
  660. ;               ZF = NO KEYS IN BUFFER (AX PRESERVED)
  661. ;               NZ = KEY IN BUFFER (RETURNED IN AX, KEY STILL IN BUFFER)
  662. ; DESCRIPTION:  CHECK BUFFER FOR ANY PENDING KEYS, RETURN KEY IF ANY
  663. ;
  664. ; NOTE: ALL OTHER AX REQUEST CODES ARE PASSED ON TO BIOS INT16H HANDLER.
  665. ;
  666. ; NOTE: DURING INT28 POPUP (InPopup AND NOT InDosClr) FUNCTIONS AH=0 AND
  667. ;       AH=1 WILL ISSUE INT28, UNLESS InDos HAS FROM VALUE AT POPUP OR
  668. ;       CritErr HAS BEEN SET.
  669. ;==============================================================================
  670.  
  671. OurInt16        proc    far
  672.                 assume  ds:nothing, es:nothing, ss:nothing
  673.  
  674.                 sti                             ;we'll manage INTs
  675.                 pushf                           ;save callers flags
  676.                 cmp     ax,GetId                ;return ID request?
  677.                 jne     NotId16                 ;NO - jump if not
  678.  
  679. ; ------------  TSR DIAGNOSTIC REQUEST, RETURN SPECIAL VALUE TO SAY WE ARE HERE
  680.  
  681.                 mov     ax,MyId                 ;ID val returned in AX
  682.                 popf                            ;restore flags
  683.                 iret                            ;return to caller
  684.  
  685. ; ------------  PASS CONTROL TO BIOS, FLAGS ON STACK
  686.  
  687. GoBios16:       popf                            ;restore flags at INT time
  688.                 jmp     OldInt16                ;continue in the woods
  689.  
  690. ; ------------  REGULAR BIOS INT16 REQUEST, CHECK FOR ANY FANCY ACTIONS
  691.  
  692. NotId16:        test    TsrMode,InPopup         ;are we in a popup?
  693.                 jz      GoBios16                ;NO - leave rest with BIOS
  694.                 test    TsrMode,InDosClr        ;InDos clear at popup?
  695.                 jnz     GoBios16                ;YES - no need to signal INT28
  696.  
  697.                 popf                            ;restore original flags
  698.                 push    bx                      ;we need a few regs here
  699.                 push    cx
  700.                 push    si
  701.                 push    ds
  702.                 pushf                           ;original flags back on stack
  703.  
  704. ; ------------  GET REQUEST CODE TO BH ENHANCED BIT TO BL
  705.  
  706.                 mov     bh,ah                   ;BH=function request code
  707.                 and     bh,not 10h              ;zap enhanced kybd bit
  708.                 cmp     bh,1                    ;any function above 1?
  709.                 ja      ExitBios16              ;YES - leave rest with BIOS
  710.  
  711.                 mov     bl,ah                   ;BL used for enhanced bit
  712.                 and     bl,10h                  ;BL=value of enhanced bit
  713.  
  714. ; ------------  GET InDos To CL, CritErr to CH, SETUP REGS
  715.  
  716.                 assume  ds:nothing
  717.  
  718.                 lds     si,InDosPtr             ;DS:[SI]=InDos
  719.                 mov     cl,byte ptr [si]        ;CL=InDos value
  720.                 lds     si,CritErrPtr           ;ES:[SI]=CritErr
  721.                 mov     ch,byte ptr [si]        ;CH=CritErr value
  722.  
  723.                 mov     si,ax                   ;save AX call value
  724.  
  725.                 mov     ax,cs                   ;move DS here, now we got it
  726.                 mov     ds,ax
  727.                 assume  ds:Cseg                 ;everybody should know
  728.  
  729. ; ------------  CHECK KEYBOARD BUFFER, ORIGINAL FLAGS ON STACK
  730.  
  731. Wait16:         mov     ah,1                    ;AH=1=test buffer status
  732.                 or      ah,bl                   ;maintain enhanced bit value
  733.  
  734.                 popf                            ;restore original flags
  735.                 pushf                           ;simulate INT
  736.                 cli                             ;in case others forgot
  737.                 call    OldInt16                ;now, ZF set if no keys
  738.                 pushf                           ;save result flags
  739.                 jnz     TestSkip16              ;jump if a key was found
  740.  
  741. ; ------------  NO KEY FOUND, CALL INT28 IF DOS InDos ALLOWS
  742.  
  743.                 cmp     cx,0001h                ;CritErr=0, InDos=1 ?
  744.                 jne     NextKey16               ;NO - wait for next key
  745.                 or      TsrMode,In28Call        ;tell people we called this INT
  746.                 int     28h                     ;now take your chance
  747.                 and     TsrMode,not In28Call    ;end of that call
  748.  
  749. ; ------------  TEST BUFFER AGAIN IF INT16.00, IRET IF INT16.01
  750.  
  751. NextKey16:      or      bh,bh                   ;is this a wait for key?
  752.                 jz      Wait16                  ;YES - then go wait for it!
  753.                 mov     ax,si                   ;restore original AX contents
  754.                 jmp     short Exit16            ;NO - exit with status we got
  755.  
  756. ; ------------  KEY IN BUFFER, IF CTRL-C WE MAY HAVE TO SKIP IT, FLAGS ON STACK
  757.  
  758. TestSkip16:     cmp     al,3                    ;is this Ctrl-C?
  759.                 jne     TestExit16              ;NO - determine exit method
  760.                 test    cx,not 0001h            ;anything but InDos=1?
  761.                 jz      TestExit16              ;NO - determine exit method
  762.  
  763. ; ------------  SKIP CTRL-C IN KEYBOARD BUFFER
  764.  
  765.                 mov     ah,bl                   ;AH=0 + enhanced bit
  766.                 popf                            ;restore original INTs
  767.                 pushf                           ;save again
  768.                 pushf                           ;simulate INT
  769.                 cli                             ;simulate properly!
  770.                 call    OldInt16                ;now, key should be gone
  771.                 jmp     short Wait16            ;do as if nothing had happened
  772.  
  773. ; ------------  KEY IN AX, IRET IF INT16.01, LEAVE WITH BIOS IF INT16.00
  774.  
  775. TestExit16:     or      bh,bh                   ;is this a wait for key?
  776.                 jnz     Exit16                  ;NO - do fast return
  777.                 mov     ax,si                   ;YES - restore AX code
  778.  
  779. ; ------------  PASS CONTROL TO BIOS, FLAGS & REGS ON STACK
  780.  
  781.                 assume  ds:nothing
  782.  
  783. ExitBios16:     popf                            ;restore work flags
  784.                 pop     ds                      ;restore regs
  785.                 pop     si
  786.                 pop     cx
  787.                 pop     bx
  788.                 cli                             ;should look like an INT
  789.                 jmp     OldInt16                ;leave rest with BIOS
  790.  
  791. ; ------------  RETURN FROM INT16, FLAGS & REGS ON STACK
  792.  
  793.                 assume  ds:nothing
  794.  
  795. Exit16:         popf                            ;restore proper flags
  796.                 pop     ds                      ;restore regs
  797.                 pop     si
  798.                 pop     cx
  799.                 pop     bx
  800.                 ret     2                       ;IRET, without flags restore
  801.  
  802. OurInt16        endp
  803.  
  804. ;==============================================================================
  805. ; OurInt21 - INT21 FILTER TO THROW DANGEROUS DOS CALLS ON CRITICAL STACK
  806. ;
  807. ; CHECK IF InPopup AND InDosClr
  808. ; CHECK FUNCTION USES CONSOLE STACK
  809. ; SET CritErr IN DOS IF CONSOLE STACK USED
  810. ; CALL OldInt21
  811. ; RESTORE CritErr IF CRITICAL STACK USED
  812. ;==============================================================================
  813.  
  814. OurInt21        proc    far
  815.                 assume  ds:nothing, es:nothing
  816.  
  817.                 pushf                           ;save calling flags
  818.                 sti
  819.  
  820.                 test    TsrMode,InPopup         ;are we in a popup?
  821.                 jz      GoDos21                 ;NO - don't worry then
  822.                 test    TsrMode,InDosClr        ;console stack idle?
  823.                 jnz     GoDos21                 ;YES - nothing fancy then
  824.  
  825. ; ------------  THIS IS 2ND CALL INTO DOS, SEE IF USING CONSOLE STACK
  826.  
  827.                 cmp     ah,0ch                  ;any function 00-0C?
  828.                 jbe     UseCrit21               ;YES - use critical stack
  829.                 test    TsrMode,NewDos          ;NO - is this DOS 3.x?
  830.                 jnz     GoDos21                 ;YES - no other to worry about
  831.                 cmp     ah,50h                  ;set PSP function?
  832.                 je      UseCrit21               ;YES - use critical stack
  833.                 cmp     ah,51h                  ;get PSP function?
  834.                 jne     GoDos21                 ;NO - leave it with DOS
  835.  
  836. ; ------------  FORCE USE OF CRITICAL STACK FOR THIS CALL
  837.  
  838. UseCrit21:      assume  ds:nothing              ;nothing to say about DS
  839.  
  840.                 push    si                      ;save regs
  841.                 push    ds
  842.                 lds     si,CritErrPtr           ;now, DS:[SI]=InDos
  843.                 mov     byte ptr [si],-1        ;FF=use crit stack now
  844.                 pop     ds                      ;restore regs
  845.                 pop     si
  846.  
  847.                 popf                            ;retsore flags setting
  848.                 pushf                           ;simulate INT
  849.                 cli                             ;in case others forgot
  850.                 call    OldInt21                ;flags already on stack
  851.  
  852.                 push    si                      ;save regs
  853.                 push    ds
  854.                 lds     si,CritErrPtr           ;now, DS:[SI]=InDos
  855.                 mov     byte ptr [si],0         ;0=back to default stack
  856.                 pop     ds                      ;restore regs
  857.                 pop     si
  858.  
  859.                 ret     2                       ;IRET throw old flags
  860.  
  861. ; ------------  PASS CONTROL TO DOS, FLAGS ON STACK
  862.  
  863. GoDos21:        popf                            ;restore original flags
  864.                 cli                             ;just in case someone forgot
  865.                 jmp     OldInt21                ;let DOS handle the rest
  866. OurInt21        endp
  867.  
  868. ;==============================================================================
  869. ; OurInt24 - SAFE DOS CRITICAL ERROR HANDLER
  870. ; IF DOS 3.X, FAIL THE SYSTEM CALL
  871. ; IF NOT DOS 3.X, IGNORE ERROR
  872. ;==============================================================================
  873.  
  874. OurInt24        proc    far
  875.                 assume  ds:nothing, es:nothing, ss:nothing
  876.  
  877.                 mov     al,3                    ;AL=3=fail system call
  878.                 test    TsrMode,NewDos          ;are we using DOS 3.x?
  879.                 jnz     Exit24                  ;YES - OK to use AL=3
  880.                 xor     al,al                   ;NO - have to ignore err then
  881. Exit24:         iret                            ;return to DOS
  882. OurInt24        endp
  883.  
  884. ;==============================================================================
  885. ; OurInt28 - TSR INT28H HANDLER, ALLOWS POPUP DURING DOS IDLE CALLS
  886. ;
  887. ; CALL OldInt28
  888. ; CHECK FOR RECURSION INTO CRITICAL INT28 CODE (& OTHER INTs AS WELL)
  889. ; SET InInt28 FLAG
  890. ; CHECK FOR HOTKEY
  891. ; CHECK IF SAFE TO POPUP
  892. ; DO POPUP IF SAFE AT THIS TIME
  893. ; RESET InInt28 FLAG
  894. ; DO IRET
  895. ;==============================================================================
  896.  
  897. ; ------------  NEAR JUMP DESTINATION FOR FAST IRET'S
  898.  
  899. Exit28:         iret                            ;IRET (!)
  900.  
  901. ; ------------  ACTUAL INT28 ENTRY POINT
  902.  
  903. OurInt28        proc    far
  904.                 assume  ds:nothing, es:nothing, ss:nothing
  905.  
  906.                 pushf
  907.                 cli                             ;in case others forgot it
  908.                 call    OldInt28                ;call TSRs loaded before this
  909.  
  910. ; ------------  ENSURE NO RECURSION ON CRITICAL INT28 CODE
  911.  
  912.                 sti                             ;we'll manage INT's after this
  913.                 test    TsrMode,InInt08 OR InInt28 OR In28Call OR InPopup
  914.                 jnz     Exit28                  ;exit fast if already going
  915.                 or      TsrMode,InInt28         ;tell'em we are here
  916.  
  917. ; ------------  CHECK FOR POSSIBLE POPUP ACTIONS
  918.  
  919.                 test    KeyMode,HotKeyOn        ;any hotkeys pressed?
  920.                 jz      ExitInt28               ;NO - don't check any more then
  921.  
  922. ; ------------  HOTKEY WAS PRESSED, ENSURE IT'S SAFE TO DO POPUP
  923.  
  924.                 call    TestSafe                ;now, CY clear if popup is OK
  925.                 jc      ExitInt28               ;jump if not to popup
  926.  
  927. ; ------------  SEEMS OK TO DO POPUP, SO DO!
  928.  
  929.                 and     KeyMode,not HotKeyOn    ;clear hotkey status
  930.                 or      TsrMode,InPopup         ;tell'em we enter popup routine
  931.                 and     TsrMode,not InInt28     ;OK to enter critical INT28
  932.                 call    InitPopup               ;then do popup
  933.                 or      TsrMode,InInt28         ;back in INT28 code here
  934.                 and     TsrMode,not InPopup     ;not in popup code any more
  935.                 and     KeyMode,not HotKeyOn    ;clear hotkeys during popup
  936.  
  937. ; ------------  NORMAL INT28H EXIT, RESET InInt28 FLAG
  938.  
  939. ExitInt28:      and     TsrMode,not InInt28     ;tell'em we left this code
  940.                 iret                            ;we have nothing more to say
  941. OurInt28        endp
  942.  
  943. ;==============================================================================
  944. ; NopInt - DUMMY IRET INSTRUCTION USED BY EMPTY INT HANDLERS
  945. ;==============================================================================
  946.  
  947. NopInt:         iret                            ;immediate return
  948.  
  949.  
  950. ;==============================================================================
  951. ; DE-INSTALL THIS TSR
  952. ;
  953. ;             NOTE!
  954. ; ***this is only supposed to be***
  955. ; ***invoked at a DOS prompt!!!!***
  956. ;
  957. ; - RESTORE INTERRUPT VECTORS
  958. ; - GIVE BACK THE MEMORY WE STOLE
  959. ; - DO A NORMAL "4C" RETURN TO DOS
  960. ;
  961. ;==============================================================================
  962.  
  963. DeInstall       proc    near
  964.                 push    ds
  965.  
  966.                 mov     bx,OurPSP               ;restore PSP to point
  967.                 mov     ax,5000h                ;to us
  968.                 int     21h
  969.  
  970.                 pop     ds
  971.                 push    ds
  972.                 mov     ax, 2528h
  973.                 lds     dx, OldInt28
  974.                 int     21h                     ;give back dos idle
  975.  
  976.                 pop     ds
  977.                 push    ds
  978.                 mov     ax, 2521h
  979.                 lds     dx, OldInt21
  980.                 int     21h                     ;give back dos funct call
  981.  
  982.                 pop     ds
  983.                 push    ds
  984.                 mov     ax, 2516h
  985.                 lds     dx, OldInt16
  986.                 int     21h                     ;give back keyboard int
  987.  
  988.                 pop     ds
  989.                 push    ds
  990.                 mov     ax, 2513h
  991.                 lds     dx, OldInt13
  992.                 int     21h                     ;give back disk int
  993.  
  994.                 pop     ds
  995.                 push    ds
  996.                 mov     ax, 2509h
  997.                 lds     dx, OldInt09
  998.                 int     21h                     ;give back kbd handler
  999.  
  1000.                 pop     ds
  1001.                 push    ds
  1002.                 mov     ax, 2508h
  1003.                 lds     dx, OldInt08
  1004.                 int     21h                     ;give back timer tick
  1005.  
  1006.                 pop     ds
  1007.                 push    ds
  1008.                 mov     ax, 2523h
  1009.                 lds     dx, PrevInt23
  1010.                 int     21h                     ;reset ^C handler
  1011.  
  1012.                 pop     ds
  1013.  
  1014.                 push    es
  1015.  
  1016.                 push    cs
  1017.                 pop     es                      ;free this block
  1018.                 mov     ah, 49h
  1019.                 int     21h
  1020.  
  1021.                 mov     es, EnvSeg
  1022.                 mov     ah, 49h                 ;free environment block
  1023.                 int     21h
  1024.  
  1025.                 pop     es
  1026.                 mov     ax, 4C00h
  1027.                 int     21h
  1028.  
  1029. DeInstall       endp
  1030.  
  1031. ;==============================================================================
  1032. ; SCREEN SAVE/RESTORE ROUTINES
  1033. ;==============================================================================
  1034. scrnhold        dw      2000 dup (?)
  1035. videoflag       dw      0
  1036. videocard       dd      0
  1037. savearea        dd      0
  1038.  
  1039. cursorsave      dw      0
  1040. cursortype      dw      0
  1041.  
  1042.  
  1043. fromscr proc    near
  1044.         push    ds              ; Save DS
  1045.         push    es              ;  and ES
  1046.         push    bp              ;  and BP
  1047.         mov     ax, videoflag
  1048.         mov     dx,3dah         ; Point DX to CGA status port
  1049.         les     di, savearea    ; Dest pointer into ES:DI
  1050.         mov     cx, 2000        ; Length value into CX
  1051.         lds     si, videocard   ; Source pointer into DS:SI
  1052.         cld                     ; Set string direction to forward
  1053.         cmp     ax,1            ; mono?
  1054.         jne     .3              ; no, have to watch for flicker
  1055.    rep  movsw                   ; yes, do a fast move
  1056.         jmp     fromscrexit     ;
  1057.  
  1058. .3:     in      al,dx           ; Get 6845 status
  1059.         rcr     al,1            ; Check horizontal retrace
  1060.         jb      .3              ; Loop if in horizontal retrace: this prevents
  1061.                                 ; starting in mid-retrace, since there is
  1062.                                 ; exactly enough time for 1 and only 1 LODSW
  1063.                                 ; during horizontal retrace
  1064.         cli                     ; No ints during critical section
  1065. .4:     in      al,dx           ; Get 6845 status
  1066.         rcr     al,1            ; Check for horizontal retrace: LODSW is 1
  1067.                                 ; clock cycle slower than STOSW; because of
  1068.                                 ; this, the vertical retrace trick can't be
  1069.                                 ; used because it causes flicker!  (RCR AL,1
  1070.                                 ; is 1 cycle faster than AND AL,AH)
  1071.         jnb     .4              ; Loop if not in retrace
  1072.         lodsw                   ; Load the video word
  1073.         sti                     ; Allow interrupts
  1074.         stosw                   ; Store the video word
  1075.         loop    .3              ; Go do next word
  1076. fromscrexit:
  1077.         pop     bp              ; Restore BP
  1078.         pop     es              ;     and ES
  1079.         pop     ds              ;     and DS
  1080.  
  1081.         mov     ah, 3
  1082.         mov     bh, 0
  1083.         int     10h
  1084.         mov     cursorsave, dx
  1085.         mov     cursortype, cx
  1086.  
  1087.         ret
  1088. fromscr endp
  1089.  
  1090.  
  1091.  
  1092. toscr   proc    near
  1093.         push    ds              ; Save DS
  1094.         push    es              ;  and ES
  1095.         push    bp              ;  and BP
  1096.         mov     ax, videoflag
  1097.         mov     dx,3dah         ; Point DX to CGA status port
  1098.         les     di, videocard   ; Dest pointer into ES:DI
  1099.         mov     cx,2000         ; Length value (words) into CX
  1100.         lds     si, savearea    ; Source pointer into DS:SI
  1101.         cld                     ; Set string direction to forward
  1102.         cmp     ax,1            ; mono?
  1103.         jne     .0              ; no, do flicker trick
  1104.   rep   movsw                   ; yes, do it fast
  1105.         jmp     toscrexit
  1106.  
  1107. .0:     lodsw                   ; Grab a video word
  1108.         mov     bp,ax           ; Save it in BP
  1109.         mov     ah,9            ; Move horiz. + vertical retrace mask
  1110. .1:     in      al,dx           ; Get 6845 status
  1111.         rcr     al,1            ; Check horizontal retrace
  1112.         jb      .1              ; Loop if in horizontal retrace: this prevents
  1113.                                 ; starting in mid-retrace, since there is
  1114.                                 ; exactly enough time for 1 and only 1 STOSW
  1115.                                 ; during horizontal retrace
  1116.         cli                     ; No ints during critical section
  1117. .2:     in      al,dx           ; Get 6845 status
  1118.         and     al,ah           ; Check for both kinds of retrace: IF the
  1119.                                 ; video board does not report horizontal
  1120.                                 ; retrace while in vertical retrace, this
  1121.                                 ; will allow several characters to be
  1122.                                 ; stuffed in during vertical retrace
  1123.         jz      .2              ; 
  1124.         mov     ax,bp           ; Get the video word
  1125.         stosw                   ; Store the video word
  1126.         sti                     ; Allow interrupts
  1127.         loop    .0              ; Go do next word
  1128.  
  1129. toscrexit:
  1130.         pop     bp              ; Restore BP
  1131.         pop     es              ;     and ES
  1132.         pop     ds              ;     and DS
  1133.  
  1134.         mov     dx, cs:cursorsave
  1135.         mov     ah, 2
  1136.         mov     bh, 0
  1137.         int     10h
  1138.         mov     cx, cs:cursortype
  1139.         mov     ah, 1
  1140.         int     10h
  1141.  
  1142.         ret
  1143. toscr   endp
  1144.  
  1145. ;==============================================================================
  1146. ; CLS---CLEAR THE SCREEN
  1147. ;==============================================================================
  1148.  
  1149.  
  1150. CLS     proc    near            ; do a BIOS screen clear
  1151.         push    ax
  1152.         push    bx
  1153.         push    cx
  1154.         push    dx
  1155.         push    bp
  1156.         mov     ax, 0600h
  1157.         mov     cx, 0
  1158.         mov     dx, 184Fh
  1159.         mov     bh, 07h
  1160.         int     10h
  1161.         pop     bp
  1162.         pop     dx
  1163.         pop     cx
  1164.         pop     bx
  1165.         pop     ax
  1166.         ret
  1167. CLS     endp
  1168.  
  1169.  
  1170. ;==============================================================================
  1171. ; GOTOXY---PUT THE CURSOR SOMEWHERE
  1172. ;==============================================================================
  1173.  
  1174. Gotoxy  proc    near            ; row, column is already in DH, DL
  1175.         push    ax
  1176.         push    bx
  1177.         mov     ah, 2
  1178.         mov     bh, 0
  1179.         int     10h
  1180.         pop     bx
  1181.         pop     ax
  1182.         ret
  1183. Gotoxy  endp
  1184.  
  1185. ; --------------------------------------
  1186. ; CURSORON---MAKES THE CURSOR VISIBLE.
  1187. ; --------------------------------------
  1188.  
  1189. CursorOn proc   near
  1190.         mov     cx, 0708h
  1191.         push    cx
  1192.         mov     ah, 0Fh
  1193.         int     10h
  1194.         pop     cx
  1195.         cmp     al, 7
  1196.         je      COmono
  1197. COsetit:
  1198.         mov     ah, 1
  1199.         int     10h
  1200.         ret
  1201. COmono:
  1202.         mov     cx, 0D0Eh
  1203.         jmp     COsetit
  1204. CursorOn endp
  1205.  
  1206.  
  1207.  
  1208. ;==============================================================================
  1209. ; InitPopup - PREPARES SYSTEM FOR POPUP, THEN CALLS Popup, THEN RESTORES
  1210. ;
  1211. ; ESTABLISH INTERNAL WORK STACK
  1212. ; SAVE CPU REGS
  1213. ; UPDATE InDosClr FLAG WITH CURRENT VALUE OF InDos
  1214. ; SAVE PROCESS RELATED SYSTEM INFO
  1215. ; SAVE USER INTERRUPT VECTORS
  1216. ; INSERT SAFE USER INTERRUPT VECTORS
  1217. ; SAVE CURRENT SCREEN 
  1218. ; CALL POPUP ROUTINE
  1219. ; RESTORE SCREEN
  1220. ; RESTORE USER INTERRUPT VECTORS
  1221. ; RESTORE PROCESS AND SYSTEM INFO
  1222. ; CLEAR InDosClr FLAG TO PREVENT UNSAFE INT28 CALLs
  1223. ; SEE IF DE-INSTALL SHOULD BE DONE (GetOutFlag==1)
  1224. ; RESTORE CPU REGS
  1225. ;==============================================================================
  1226.  
  1227. InitPopup       proc    near
  1228.                 assume  ds:nothing, es:nothing, ss:nothing
  1229.  
  1230. ; ------------  SWITCH TO PSP INTERNAL STACK
  1231.  
  1232.                 mov     OldSS,ss                ;save current stack frame
  1233.                 mov     OldSP,sp
  1234.  
  1235.                 cli                             ;always CLI for the old chips
  1236.                 mov     ss,OurSS                ;move SS here
  1237.                 mov     sp,OurSP                ;move SP into position
  1238.                 sti                             ;OK guys
  1239.  
  1240. ; ------------  SAVE ALL REGS
  1241.  
  1242.                 push    ax
  1243.                 push    bx
  1244.                 push    cx
  1245.                 push    dx
  1246.                 push    bp
  1247.                 push    si
  1248.                 push    di
  1249.                 push    ds
  1250.                 push    es
  1251.  
  1252.                 mov     ax,cs
  1253.                 mov     ds,ax                   ;mov DS here
  1254.                 assume  ds:Cseg                 ;tell MASM that
  1255.  
  1256. ; ------------  TAG VALUE OF InDos FLAG AT TIME OF POPUP
  1257.  
  1258.                 or      TsrMode,InDosClr        ;assume InDos=0
  1259.                 les     si,InDosPtr             ;now, ES:[SI]=InDos
  1260.                 cmp     byte ptr es:[si],1      ;InDos set? (>2 impossible)
  1261.                 jb      InDosSaved              ;NO - jump if all clear DOS
  1262.                 and     TsrMode,not InDosClr    ;clear flag for popup InDos
  1263. InDosSaved:
  1264.  
  1265. ; ------------  SAVE DOS 3.X EXTENDED ERROR INFO
  1266.  
  1267.                 test    TsrMode,NewDos          ;really DOS 3.x?
  1268.                 jz      Dos3Saved               ;NO - jump if not 3.x
  1269.  
  1270.                 mov     ah,59h                  ;to get err info from DOS
  1271.                 xor     bx,bx                   ;BX must be zero
  1272.                 push    ds                      ;save DS (killed by DOS)
  1273.                 int     21h                     ;ext err info in AX,BX,CX
  1274.                 pop     ds                      ;restore
  1275.                 mov     OldExtErr[0],ax         ;save
  1276.                 mov     OldExtErr[2],bx
  1277.                 mov     OldExtErr[4],cx
  1278.  
  1279. Dos3Saved:
  1280.  
  1281. ; ------------  SAVE CURRENT BREAK STATE, RELAX BREAK CHECKING
  1282.  
  1283.                 mov     ax,3302h                ;to swap DL with BREAK value
  1284.                 xor     dl,dl                   ;DL=0=relax checking
  1285.                 int     21h                     ;current level in DL
  1286.                 mov     OldBreak,dl             ;save current level
  1287.  
  1288. ; ------------  SAVE CURRENT USER INT VECTORS
  1289.  
  1290.                 mov     ax,351bh                ;BIOS ctrl-break int
  1291.                 int     21h                     ;ES:BX=vector
  1292.                 mov     OldInt1BOff,bx          ;save it
  1293.                 mov     OldInt1BSeg,es
  1294.  
  1295.                 mov     ax,351ch                ;BIOS timer tick
  1296.                 int     21h                     ;ES:BX=vector
  1297.                 mov     OldInt1COff,bx          ;save it
  1298.                 mov     OldInt1CSeg,es
  1299.  
  1300.                 mov     ax,3523h                ;DOS ctrl-C
  1301.                 int     21h                     ;ES:BX=vector
  1302.                 mov     OldInt23Off,bx          ;save it
  1303.                 mov     OldInt23Seg,es
  1304.  
  1305.                 mov     ax,3524h                ;DOS crit err handler
  1306.                 int     21h                     ;ES:BX=vector
  1307.                 mov     OldInt24Off,bx          ;save it
  1308.                 mov     OldInt24Seg,es
  1309.  
  1310. ; ------------  INSERT DUMMY IRET INTO DANGEROUS VECTORS
  1311.  
  1312.                 mov     dx,offset NopInt        ;now, DS:DX=dunny iret
  1313.                 mov     ax,251bh                ;BIOS ctrlk-break handler
  1314.                 int     21h                     ;set to IRET
  1315.                 mov     ax,251ch                ;BIOS timer tick
  1316.                 int     21h                     ;set to IRET
  1317.                 mov     ax,2523h                ;DOS ctrl-C handler
  1318.                 int     21h                     ;set to IRET
  1319.  
  1320. ; ------------  ESTABLISH SAFE CRITICAL ERROR HANDLER
  1321.  
  1322.                 mov     dx,offset OurInt24      ;now, DS:DX=safe crit err
  1323.                 mov     ax,2524h                ;to set crit err handler
  1324.                 int     21h
  1325.  
  1326. ; ------------  SAVE CURRENT DTA AREA, SET OUR DEFAULT DTA
  1327.  
  1328.                 mov     ah,2fh                  ;to obtain current DTA from DOS
  1329.                 int     21h                     ;DTA addr now in ES:BX
  1330.                 mov     OldDTAOff,bx            ;save it
  1331.                 mov     OldDTASeg,es
  1332.  
  1333.                 push    ds                      ;save DS for a while
  1334.                 lds     dx,OurDTA               ;DS:DX=our DTA addr
  1335.                 mov     ah,1ah                  ;to set DTA via DOS
  1336.                 int     21h                     ;set that addr
  1337.                 pop     ds                      ;restore DS
  1338.  
  1339. ; ------------  SAVE CURRENT PSP, ESTABLISH OURS INSTEAD
  1340.  
  1341.                 mov     ax,5100h                ;to get PSP from DOS
  1342.                 int     21h                     ;current PSP now in BX
  1343.                 mov     OldPSP,bx               ;save it
  1344.                 mov     bx,OurPSP               ;het our PSP instead
  1345.                 mov     ax,5000h                ;to set our PSP
  1346.                 int     21h
  1347.  
  1348. ; ------------  CALL USER POPUP ROUTINE
  1349.  
  1350.                 call    FromScr                 ;save screen
  1351.                 call    Popup                   ;finally!
  1352.                 call    ToScr                   ;put screen back
  1353.  
  1354. ; ------------  RESTORE TO SAVED CURRENT PROCESS
  1355.  
  1356.                 mov     bx,OldPSP               ;new current process in BX
  1357.                 mov     ax,5000h                ;to set PSP via DOS
  1358.                 int     21h                     ;restore original PSP
  1359.  
  1360. ; ------------  RESTORE SAVED DTA
  1361.  
  1362.                 push    ds                      ;save DS for a while
  1363.                 lds     dx,OldDTA               ;DS:DX=our DTA addr
  1364.                 mov     ah,1ah                  ;to set DTA via DOS
  1365.                 int     21h                     ;set that addr
  1366.                 pop     ds                      ;restore DS
  1367.  
  1368. ; ------------  RESTORE SAVED INTERRUPT VECTORS
  1369.  
  1370.                 push    ds                      ;save for a while
  1371.                 assume  ds:nothing              ;be careful about MASM
  1372.  
  1373.                 lds     dx,OldInt1B             ;BIOS ctrl-break handler
  1374.                 mov     ax,251bh
  1375.                 int     21h
  1376.  
  1377.                 lds     dx,OldInt1C             ;BIOS timer tick
  1378.                 mov     ax,251ch
  1379.                 int     21h
  1380.  
  1381.                 lds     dx,OldInt23             ;ctrl-C handler (an IRET)
  1382.                 mov     ax,2523h
  1383.                 int     21h
  1384.  
  1385.                 lds     dx,OldInt24             ;DOS crit err handler
  1386.                 mov     ax,2524h
  1387.                 int     21h
  1388.  
  1389.                 pop     ds                      ;restore data seg DS
  1390.                 assume  ds:Cseg
  1391.  
  1392. ; ------------  RESTORE SAVED BREAK CHECKING LEVEL
  1393.  
  1394.                 mov     ax,3301h                ;to set break check level
  1395.                 mov     dl,OldBreak             ;get saved break state
  1396.                 int     21h
  1397.  
  1398. ; ------------  RESTORE DOS 3.X SPECIFIC SYSTEM INFO
  1399.  
  1400.                 test    TsrMode,NewDos          ;using DOS 3.x
  1401.                 jz      Dos3Restored            ;NO - jump if old DOS 2
  1402.                 mov     dx,offset OldExtErr     ;DS:DX=3 words of ext err
  1403.                 mov     ax,5d0ah                ;to set ext err info
  1404.                 int     21h
  1405. Dos3Restored:
  1406.  
  1407. ; ------------  RESET InDosSet FLAG VALUE TO PREVENT UNSAFE INT28
  1408.  
  1409.                 or      TsrMode,InDosClr        ;now we only care that InDos=0
  1410.  
  1411. ; ------------  DO WE WANT TO DE-INSTALL?
  1412.  
  1413.                 cmp     GetOutFlag, 0           ;set in POPUP if user said
  1414.                 je      RestoreTheRegs          ;to remove ourselves
  1415.  
  1416.                 jmp     DeInstall               ;go die gracefully
  1417.  
  1418. ; ------------  RESTORE USER REGS
  1419.  
  1420. RestoreTheRegs:
  1421.                 pop     es
  1422.                 pop     ds
  1423.                 pop     di
  1424.                 pop     si
  1425.                 pop     bp
  1426.                 pop     dx
  1427.                 pop     cx
  1428.                 pop     bx
  1429.                 pop     ax
  1430.                 assume  ds:nothing
  1431.  
  1432. ; ------------  RETURN TO USER STACK
  1433.  
  1434.                 cli                             ;always CLI for the old chips
  1435.                 mov     ss,OldSS                ;restore SS
  1436.                 mov     sp,OldSP                ;restore SP
  1437.                 sti                             ;OK guys
  1438.  
  1439.                 ret
  1440. InitPopup       endp
  1441.  
  1442. ;==============================================================================
  1443. ; Popup - POPUP USER ROUTINE
  1444. ;
  1445. ; ALL REGISTERS EXCEPT SS:SP AND DS MAY BE CHANGED.
  1446. ; DS IS PRESET TO THE TSR DATA SEGMENT.
  1447. ;
  1448. ; NOTE: UPON ENTRY TO THIS ROUTINE ALL DOS FUNCTIONS MAY BE CALLED.
  1449. ;       IF POPUP WAS DONE ON INT28, WITH CritErr-1, ALL DOS FUNCTIONS
  1450. ;       THAT WOULD NORMALLY USE THE CONSOLE STACK, WILL GO TO THE CRITICAL
  1451. ;       STACK, HENCE PREVENTING FURTHER POPUP DURING THE DOS CALL.
  1452. ;       (HOWEVER, MOST TSRs WOULD NOT POPUP ANYWAY, SINCE InDos-2).
  1453. ;
  1454. ;       ADDRESSES OF THE InDos AND CritErr ARE STORED IN THE DOUBLE WORDS
  1455. ;       InDosPtr AND CritErrPtr.
  1456. ;
  1457. ;       AT ENTRY CritErr FLAG IS 0 (ZERO), InDos NO GREATER THAN 1 (ONE).
  1458. ;
  1459. ;       THE SCREEN HAS ALREADY BEEN SAVED (BUT NOT CLEARED).
  1460. ;       IT WILL BE RESTORED AUTOMATICALLY UPON EXIT.
  1461. ;
  1462. ;==============================================================================
  1463.  
  1464. ImageHandle     dw      0
  1465. ImageName       db      "C:\Memory.Img", 0        ;in root directory
  1466.  
  1467. NamePrompt      db      13, 10
  1468.                 db      "Enter name of program to execute: "
  1469. NamePromptLen   equ     $-NamePrompt
  1470.  
  1471. WaitMsg1        db      13, 10, 13, 10
  1472.                 db      "Swapping out...."
  1473. WaitMsgLen1     equ     $-WaitMsg1
  1474.  
  1475. KeyWaitMsg      db      13, 10
  1476.                 db      "Press a key to continue."
  1477. KeyWaitMsgLen   equ     $-KeyWaitMsg
  1478.  
  1479. WaitMsg2        db      13, 10, 13, 10
  1480.                 db      "Swapping in...."
  1481. WaitMsgLen2     equ     $-WaitMsg2
  1482.  
  1483. CRLFMsg         db      13, 10
  1484. CRLFMsgLen      equ     $-CRLFMsg
  1485.  
  1486. PrevSS          dw      0
  1487. PrevSP          dw      0
  1488. PrevDS          dw      0
  1489. PrevES          dw      0
  1490. Partition       dw      0
  1491.  
  1492. CmdLin          db      80 dup(0)
  1493.  
  1494.  
  1495. Popup           proc    near
  1496.                 assume  ds:Cseg, es:nothing, ss:nothing
  1497.  
  1498. ; ------------  IF NO PROGRAM IS ACTIVE, DON'T SAVE 192K
  1499. CheckCurrPgm:
  1500.                 mov     ax, cs                  ;point to our MCB
  1501.                 dec     ax                      ;(is 16 bytes below PSP)
  1502.                 mov     es, ax                  ;'Z' means last one
  1503. CheckThisMCB:
  1504.                 cmp     Byte Ptr es:[0000], "Z" ;MCB signature byte
  1505.                 je      DoLastMCB               ;this is last in chain
  1506.                 cmp     Byte Ptr es:[0000], "M" ;'M' is other possibility
  1507.                 je      GotoNextMCB             ;try the next one
  1508.  
  1509.                 jmp     PopExit                 ;MCB's are corrupted.
  1510.  
  1511. GotoNextMCB:
  1512.                 add     ax, es:[0003]           ;length of this block
  1513.                 inc     ax                      ;plus 1
  1514.                 mov     es, ax                  ;points to next MCB
  1515.                 jmp     CheckThisMCB
  1516.  
  1517. DoLastMCB:
  1518.                 mov     ax, Word Ptr es:[0003]  ;get the length
  1519.                 cmp     ax, 12288               ;is it size we need? (paras)
  1520.                 ja      DoThisBlock             ;yep.
  1521.  
  1522.                 jmp     PopExit                 ;nope; don't get fancy
  1523.  
  1524. DoThisBlock:
  1525.                 mov     ApplPSP, 0              ;default to "it's free"
  1526.                 mov     bx, Word Ptr es:[0001]  ;get owner of block
  1527.                 cmp     bx, 0                   ;is it a free block?
  1528.                 jne     SetupForSwap            ;no, have to swap it out
  1529.  
  1530.                 jmp     ExecIt                  ;free block (don't swap)
  1531.  
  1532. SetupForSwap:
  1533.                 mov     ax, es
  1534.                 inc     ax                      ;point up to PSP
  1535.                 mov     ApplPSP, ax
  1536.  
  1537. ; ------------  IF COMMAND.COM OWNS THAT BLOCK, FORGET IT.
  1538.  
  1539.                 mov     ax, cs                  ;point to us
  1540.                 cmp     ax, bx                  ;owner of block we want
  1541.                 jbe     SwapMemory              ;it's okay to use Command
  1542.  
  1543.                 call    ErrBeep                 ;oh-oh, we can't do anything
  1544.                 jmp     PopExit
  1545.  
  1546. ; ------------  SAVE 192K OF UPPER MEMORY IN FILE "MEMORY.IMG"
  1547. SwapMemory:
  1548.                 call    CLS
  1549.  
  1550.                 mov     cx, 80
  1551.                 mov     di, offset CmdLin
  1552.                 mov     al, 0
  1553.         rep     stosb
  1554.  
  1555.                 mov     ah, 40h
  1556.                 mov     bx, 1
  1557.                 mov     cx, NamePromptLen
  1558.                 mov     dx, offset NamePrompt
  1559.                 int     21h
  1560.  
  1561.                 call    CursorOn
  1562.  
  1563.                 mov     ah, 3Fh
  1564.                 mov     bx, 0
  1565.                 mov     cx, 80
  1566.                 mov     dx, offset CmdLin
  1567.                 inc     dx
  1568.                 int     21h
  1569.                 mov     CmdLin, al
  1570.  
  1571.                 mov     si, offset CmdLin
  1572.                 add     si, ax
  1573.                 mov     Byte Ptr [si], 0
  1574.  
  1575.                 mov     ah, 40h
  1576.                 mov     bx, 1
  1577.                 mov     cx, WaitMsgLen1
  1578.                 mov     dx, offset WaitMsg1
  1579.                 int     21h
  1580.  
  1581. ; ------------  FIGURE OUT HOW MUCH MEMORY THERE IS; SWAP TOP 192K
  1582.  
  1583.                 int     12h             ;should return ax=640
  1584.                 sub     ax, 192         ;subtract what we need
  1585.                 mov     cx, 6           ;shift left 6 bits in order
  1586.                 shl     ax, cl          ;to get it in paragraphs
  1587.                 mov     Partition, ax   ;remember para addr for later
  1588.  
  1589.                 mov     ah, 3Ch
  1590.                 mov     dx, offset ImageName
  1591.                 mov     cx, 0
  1592.                 int     21h             ;create memory image file
  1593.                 jnc     SaveTheHndl
  1594.  
  1595.                 call    ErrBeep         ;Error!
  1596.                 jmp     PopExit         ;unable to create file
  1597.  
  1598. SaveTheHndl:
  1599.                 mov     ImageHandle, ax ;we'll need it later
  1600. Write192K:
  1601.                 mov     PrevDS, ds      ;we fool with ds quite a bit...
  1602.                 mov     bx, ax          ;we also need handle now
  1603.                 mov     ah, 40h
  1604.                 mov     cx, 0FFF0h      ;unsigned 65520 quantity
  1605.                 mov     dx, 0
  1606.                 mov     ds, cs:Partition ;recall para addr from above
  1607.                 int     21h             ;write 64K to disk (well, almost)
  1608.                 jc      Error192        ;if I/O error...
  1609.                 cmp     ax, cx          ;disk full?
  1610.                 jne     Error192
  1611.  
  1612.                 mov     ax, ds
  1613.                 add     ax, 4095        ;bump up to next segment
  1614.                 mov     ds, ax
  1615.                 mov     ah, 40h
  1616.                 int     21h             ;write next 64K (little less, really)
  1617.                 jc      Error192        ;if I/O error...
  1618.                 cmp     ax, cx          ;disk full?
  1619.                 jne     Error192
  1620.  
  1621.                 mov     ax, ds
  1622.                 add     ax, 4095        ;final segment
  1623.                 mov     ds, ax
  1624.                 mov     ah, 40h
  1625.                 int     21h             ;write last 64K segment
  1626.                 jc      Error192        ;if I/O error...
  1627.                 cmp     ax, cx          ;disk full?
  1628.                 jne     Error192
  1629.  
  1630.                 mov     ah, 3Eh
  1631.                 int     21h             ;now close it (196,560 bytes written)
  1632.  
  1633.                 mov     ax, cs:PrevDS   ;restore our ds
  1634.                 mov     ds, ax
  1635.  
  1636.                 jmp     FreeIt
  1637.  
  1638. Error192:
  1639.                 call    ErrBeep         ;do it twice here for id
  1640.                 call    ErrBeep
  1641.                 mov     ah, 3Eh         ;close it and get out
  1642.                 int     21h
  1643.                 mov     ds, cs:PrevDS   ;who knows where it wound up??
  1644.                 jmp     PopExit
  1645.  
  1646.  
  1647. ; ------------  FREE THAT MEMORY SPACE
  1648. FreeIt:
  1649.                 push    es
  1650.                 mov     ax, 0
  1651.                 mov     es, ax
  1652.                 mov     Byte Ptr es:[050Fh], 0
  1653.                 pop     es
  1654.  
  1655.                 mov     bx, ApplPSP             ;pretend we're in Appl.
  1656.                 mov     ax,5000h                ;to set PSP via DOS
  1657.                 int     21h                     ;set Application PSP
  1658.  
  1659.                 mov     ax, ApplPSP
  1660.                 dec     ax
  1661.                 mov     es, ax          ;look at memory control block
  1662.                 mov     bx, word ptr es:[3]     ;get current allocation
  1663.                 mov     CurrentAlloc, bx        ;and save it
  1664.                 sub     bx, 12160       ;190K = 12160 paragraphs
  1665.                 mov     es, ApplPSP     ;block to be shrunk
  1666.                 mov     ah, 4Ah         ;setblock
  1667.                 int     21h             ;we now have some free memory
  1668.                 jnc     ExecIt
  1669.  
  1670.                 mov     bx,OurPSP       ;our current process in BX
  1671.                 mov     ax,5000h        ;to set PSP via DOS
  1672.                 int     21h             ;restore our PSP
  1673.  
  1674.                 call    ErrBeep         ;memory request failed
  1675.                 call    ErrBeep         ;do twice
  1676.                 jmp     PopExit         ;let's get out of here
  1677.  
  1678. ; ------------  RUN PROGRAM
  1679. ExecIt:
  1680.         push    ds
  1681.         pop     es
  1682.  
  1683.         call    CLS
  1684.  
  1685.         mov     bx,OurPSP       ;our current process in BX
  1686.         mov     ax,5000h        ;to set PSP via DOS
  1687.         int     21h             ;restore our PSP
  1688.  
  1689.         mov     cs:PrevES, es
  1690.         mov     cs:PrevDS, ds
  1691.         mov     cs:PrevSS, ss
  1692.         mov     cs:PrevSP, sp
  1693.  
  1694.         mov     si, offset CmdLin
  1695.         Int     2Eh
  1696.  
  1697.         CLI
  1698.         mov     ss, cs:PrevSS
  1699.         mov     sp, cs:PrevSP
  1700.         mov     ds, cs:PrevDS
  1701.         mov     es, cs:PrevES
  1702.         STI
  1703.  
  1704. ; ------------  GRAB THE MEMORY BACK (BY ALLOCATING ALL OF IT AGAIN)
  1705. ResetMemory:
  1706.                 cmp     ApplPSP, 0              ;is application alive?
  1707.                 jne     GetMain                 ;yes, re-alloc the memory
  1708.  
  1709.                 jmp     PopExit                 ;no, no need to re-alloc
  1710.  
  1711. GetMain:
  1712.                 mov     bx,ApplPSP              ;pretend we're the appl.
  1713.                 mov     ax,5000h                ;to set PSP via DOS
  1714.                 int     21h                     ;restore our PSP
  1715.  
  1716.                 mov     es, ApplPSP             ;point to Appl's PSP
  1717.                 mov     bx, CurrentAlloc        ;what it used to have
  1718.                 mov     ah, 4Ah                 ;ask for old amt. back
  1719.                 int     21h                     ;will DOS do it?
  1720.                 jnc     RestoreImage            ;yep, no error
  1721.  
  1722.                 call    ErrBeep                 ;realloc-memory failed
  1723.                 call    ErrBeep                 ;thrice
  1724.                 call    ErrBeep                 ;and fall through restore
  1725.  
  1726. ; ------------  RESTORE THE 192K MEMORY IMAGE FROM DISK
  1727. RestoreImage:
  1728.                 push    ds
  1729.                 pop     es
  1730.  
  1731.                 mov     ah, 40h
  1732.                 mov     dx, offset KeyWaitMsg
  1733.                 mov     cx, KeyWaitMsgLen
  1734.                 mov     bx, 1
  1735.                 int     21h
  1736.  
  1737.                 mov     ax, 0
  1738.                 int     16h
  1739.  
  1740.                 mov     ah, 40h
  1741.                 mov     dx, offset WaitMsg2
  1742.                 mov     cx, WaitMsgLen2
  1743.                 mov     bx, 1
  1744.                 int     21h
  1745.  
  1746.                 mov     bx,OurPSP       ;our current process in BX
  1747.                 mov     ax,5000h        ;to set PSP via DOS
  1748.                 int     21h             ;restore our PSP
  1749.  
  1750.                 mov     ax, 3D00h               ;"compatibility mode" open
  1751.                 mov     dx, offset ImageName
  1752.                 int     21h
  1753.                 jnc     Read192K
  1754.  
  1755.                 call    ErrBeep                 ;a "whoops!"
  1756.                 jmp     PopExit
  1757.  
  1758. Read192K:
  1759.                 mov     ImageHandle, ax         ;it may have changed
  1760.                 mov     bx, ax
  1761.                 mov     PrevDS, ds              ;so we can mess with it
  1762.                 mov     ah, 3Fh                 ;read back the image
  1763.                 mov     ds, cs:Partition        ;point to upper 192K
  1764.                 mov     dx, 0
  1765.                 mov     cx, 0FFF0h              ;same segment size as before
  1766.                 int     21h                     ;read it
  1767.                 jc      Error192r               ;if I/O error...
  1768.                 cmp     ax, cx                  ;not all bytes read?
  1769.                 jne     Error192r
  1770.  
  1771.                 mov     ax, ds
  1772.                 add     ax, 4095                ;bump up to next segment
  1773.                 mov     ds, ax
  1774.                 mov     ah, 3Fh                 ;here we go again
  1775.                 int     21h
  1776.                 jc      Error192r               ;if I/O error...
  1777.                 cmp     ax, cx                  ;not all bytes?
  1778.                 jne     Error192r
  1779.  
  1780.                 mov     ax, ds
  1781.                 add     ax, 4095                ;bump up to last segment
  1782.                 mov     ds, ax
  1783.                 mov     ah, 3Fh                 ;for the last time
  1784.                 int     21h
  1785.                 jc      Error192r               ;if I/O error...
  1786.                 cmp     ax, cx                  ;everything there?
  1787.                 jne     Error192r
  1788.  
  1789.                 mov     ah, 3Eh                 ;close it
  1790.                 int     21h
  1791.                 mov     ds, cs:PrevDS           ;set it back
  1792.  
  1793.                 jmp     PopExit                 ;all done!
  1794.  
  1795. Error192r:
  1796.                 call    ErrBeep                 ;3 times for id
  1797.                 call    ErrBeep
  1798.                 call    ErrBeep
  1799.                 mov     ah, 3Eh                 ;close error file
  1800.                 int     21h                     ;restore ds
  1801.                 mov     ds, cs:PrevDS           ;and fall through to exit
  1802.  
  1803. ; ------------  RETURN TO THE ORIGINAL APPLICATION
  1804. PopExit:
  1805.                 push    ds
  1806.                 pop     es
  1807.                 ret
  1808.  
  1809. ; ------------  UNINSTALL DOESN'T APPLY HERE
  1810.  
  1811. UnInstall:
  1812.                 mov     GetOutFlag, 1           ;will do it later
  1813.                 ret
  1814. Popup           endp
  1815.  
  1816. ;==============================================================================
  1817. ; TSR IRON CURTAIN 
  1818. ;==============================================================================
  1819.  
  1820. TsrCurtain:
  1821.  
  1822.  
  1823. ;***********************************************************
  1824.  
  1825.  
  1826.  
  1827. ;==============================================================================
  1828. ; NON-RESIDENT MESSAGES FOR INIT
  1829. ;==============================================================================
  1830.  
  1831. SecondMsg       label   byte
  1832. db      7,13,10
  1833. db      "The Context Switch program is already loaded!",13,10
  1834. db      "$"
  1835.  
  1836. HotKeyMsg       label   byte
  1837. db      13,10,13,10
  1838. db      "CONTEXT SWITCH"
  1839. db      13,10
  1840. db      "Hit <Alt Right-Shift> to invoke Context Switch."
  1841. db      13,10,13,10
  1842. db      "$"
  1843.  
  1844. Dos1Msg label   byte
  1845. db      7,13,10
  1846. db      "Must use DOS release 2.00 or later.",13,10,10
  1847. db      "$"
  1848.  
  1849. BadDosMsg       label   byte
  1850. db      7,13,10
  1851. db      "Did not recognize DOS version.",13,10,10
  1852. db      "$"
  1853.  
  1854. ; ------------  DOS ERROR LEVEL EXIT CODES
  1855.  
  1856. xOk             equ     0                       ;normal, OK exit
  1857. xSecond         equ     1                       ;TSR already loaded
  1858. xBadDos         equ     2                       ;CritErr flag not found
  1859.  
  1860. ;==============================================================================
  1861. ; DISPLAY BANNER, INITIALIZE SYSTEM DATA, CHECK IF ALREADY LOADED,
  1862. ; HOOK INTO INTERRUPT CHAIN, TERMINATE, BUT STAY RESIDENT.
  1863. ;==============================================================================
  1864.  
  1865. Init            proc    near
  1866.                 assume  ds:Cseg, es:nothing, ss:nothing
  1867.  
  1868.                 call    CLS
  1869.                 mov     dx,0
  1870.                 call    Gotoxy
  1871.  
  1872. ; ------------  USE INT16H DIAGNOSTIC TO SEE IF TSR ALREADY INSTALLED
  1873.  
  1874.                 mov     ax,GetId                ;INT16h diagnostic request
  1875.                 int     16h                     ;now, AX=MyId if installed
  1876.                 cmp     ax,MyId                 ;TSR already installed?
  1877.                 jne     CheckDos                ;NO - jump if not installed
  1878.  
  1879. ; ------------  TSR ALREADY INSTALLED, DISPLAY MSG, EXIT
  1880.  
  1881.                 mov     dx,offset SecondMsg
  1882.                 mov     ah,9
  1883.                 int     21h                     ;display already-installed
  1884.                 mov     dx,offset HotKeyMsg
  1885.                 mov     ah,9
  1886.                 int     21h                     ;be kind & disp hot key
  1887.                 mov     ax,4c00h + xSecond      ;error level in AL
  1888.                 int     21h                     ;abort now
  1889.  
  1890. ; ------------  It's only DOS 1!  Too bad.
  1891.  
  1892. Dos1:           mov     dx,offset Dos1Msg
  1893.                 mov     ah,9
  1894.                 int     21h                     ;display msg about DOS 1
  1895.                 int     20h                     ;no err level for DOS 1
  1896.  
  1897. ; ------------  ENSURE DOS VERSION IS NEWER THAN 2.00
  1898.  
  1899. CheckDos:       or      TsrMode,NewDos          ;assume suing DOS 3.x
  1900.                 mov     ah,30h                  ;to get DOS version number
  1901.                 int     21h                     ;version is AL.AH
  1902.                 cmp     al,2                    ;release 2 or newer?
  1903.                 jb      Dos1                    ;NO - jump if DOS 1 in use
  1904.                 ja      DosFlags                ;jump if DOS 3.x
  1905.                 and     TsrMode,not NewDos      ;now, say we use DOS 2.x
  1906.  
  1907. ; ------------  INITIALIZE PTRS TO DOS FLAGS - 1ST InDos
  1908.  
  1909. DosFlags:       mov     ax,3400h                ;to get InDos ptr
  1910.                 int     21h                     ;ES:BX=seg:off of InDos
  1911.                 mov     InDosOff,bx             ;save ptr
  1912.                 mov     InDosSeg,es
  1913.  
  1914. ; ------------  WE NEED CritErr TO USE PSP FUNCTIONS IN DOS 2.X
  1915.  
  1916.                 xor     dl,dl                   ;DL=0=this is 1st scan
  1917.                 mov     CritErrSeg,es           ;DOS seg still in ES
  1918. CritScan:       mov     di,bx                   ;start search at InDos
  1919.                 mov     cx,2000h                ;search max 1000h words
  1920.                 mov     ax,3e80h                ;opcode CMP BYTE PTR [CritErr]
  1921.                 cld                             ;better serach forward
  1922.  
  1923. CritScan2:      repne   scasw                   ;search till found or end
  1924.                 jne     NoCritFound             ;jump if CMP not found
  1925.                                                 ;ES:[DI-2] at:
  1926.                                                 ;       CMP BYTE PTR [CritErr]
  1927.                                                 ;       JNZ ...
  1928.                                                 ;       MOV SP,stack addr
  1929.                 cmp     byte ptr es:[di][5],0bch ;really CMP SP there?
  1930.                 jne     CritScan2               ;NO - scan again if not
  1931.                 mov     ax,word ptr es:[di]     ;now, AX=CritErr offset
  1932.                 mov     CritErrOff,ax           ;save it
  1933.                 jmp     short InitData          ;OK to end this now
  1934.  
  1935. NoCritFound:    or      dl,dl                   ;was this1 st scan?
  1936.                 jnz     BadDos                  ;NO - CritErr not founbd at all
  1937.                 inc     dl                      ;DL=1=this is 2nd scan
  1938.                 inc     bx                      ;try scan at odd/even offset
  1939.                 jmp     CritScan                ;scan again
  1940.                 
  1941. ; ------------  COULD NOT LOCATE DOS CritErr FLAG - THAT'S AN ERROR
  1942.  
  1943. BadDos:         mov     dx,offset BadDosMsg
  1944.                 mov     ah,9
  1945.                 int     21h                     ;display msg about that
  1946.                 mov     ax,4c00h + xBadDos      ;err level in AL
  1947.                 int     21h                     ;OK to use 4C (DOS >= 2)
  1948.  
  1949. ; ------------  INITIALIZE SYSTEM DATA VARIABLES
  1950.  
  1951. InitData:                                       ;store position for stack
  1952.                 mov     OurSP,TsrCurtain - ComEntry + 100h + StackSize
  1953.                 mov     OurSS,cs                ;stack seg is code seg
  1954.  
  1955.                 mov     ax,5100h                ;to get current PSP from DOS
  1956.                 int     21h                     ;PSP now in BX
  1957.                 mov     OurPSP,bx               ;save our PSP
  1958.  
  1959.                 mov     ah,2fh                  ;to get current DTA from DOS
  1960.                 int     21h                     ;now, ES:BX=current DTA
  1961.                 mov     OurDTAOff,bx            ;save it
  1962.                 mov     OurDTASeg,es
  1963.  
  1964.                 and     KeyMode,not HotIsShift  ;hotkey is not shift state
  1965.                 or      TsrMode,InDosClr        ;will prevent unsafe INT28s
  1966.  
  1967. ; ------------  SAVE VECTORS FOR OUR MONITOR INTERRUPTS
  1968.  
  1969.                 mov     ax,3508h                ;BIOS timer0 tick handler
  1970.                 int     21h                     ;ES:BX=vector
  1971.                 mov     OldInt08Off,bx
  1972.                 mov     OldInt08Seg,es
  1973.  
  1974.                 mov     ax,3509h                ;BIOS kb HW handler
  1975.                 int     21h                     ;ES:BX=vector
  1976.                 mov     OldInt09Off,bx
  1977.                 mov     OldInt09Seg,es
  1978.  
  1979.                 mov     ax,3513h                ;BIOS disk I/O service
  1980.                 int     21h                     ;ES:BX=vector
  1981.                 mov     OldInt13Off,bx
  1982.                 mov     OldInt13Seg,es
  1983.  
  1984.                 mov     ax,3516h                ;BIOS kb read
  1985.                 int     21h                     ;ES:BX=vector
  1986.                 mov     OldInt16Off,bx
  1987.                 mov     OldInt16Seg,es
  1988.  
  1989.                 mov     ax,3521h                ;DOS functions dispatcher
  1990.                 int     21h                     ;ES:BX=vector
  1991.                 mov     OldInt21Off,bx
  1992.                 mov     OldInt21Seg,es
  1993.  
  1994.                 mov     ax,3528h                ;DOS idle hook
  1995.                 int     21h                     ;ES:BX=vector
  1996.                 mov     OldInt28Off,bx
  1997.                 mov     OldInt28Seg,es
  1998.  
  1999.                 mov     ax,3523h                ;Previous ^C handler
  2000.                 int     21h                     ;ES:BX=vector
  2001.                 mov     PrevInt23Off,bx
  2002.                 mov     PrevInt23Seg,es
  2003.  
  2004.  
  2005. ; ------------  ESTABLISH IRET INT23 TO PREVENT BREAK DURING VECTOR FIX
  2006.  
  2007.                 mov     dx,offset NopInt        ;DS:DX=dummy vector to set
  2008.                 mov     ax,2523h                ;to set ^C handler through DOS
  2009.                 int     21h                     ;now, no break will occur
  2010.  
  2011. ; ------------  SAVE VECTORS FOR OUR MONITOR INTERRUPTS
  2012.  
  2013.                 mov     ax,2508h                ;to set our INT08h handler
  2014.                 mov     dx,offset OurInt08      ;DS:DX=new vector
  2015.                 int     21h                     ;let DOS set vector
  2016.  
  2017.                 mov     ax,2509h                ;to set our INT09h handler
  2018.                 mov     dx,offset OurInt09      ;DS:DX=new vector
  2019.                 int     21h                     ;let DOS set vector
  2020.  
  2021.                 mov     ax,2513h                ;to set our INT13h handler
  2022.                 mov     dx,offset OurInt13      ;DS:DX=new vector
  2023.                 int     21h                     ;let DOS set vector
  2024.  
  2025.                 mov     ax,2516h                ;to set our INT16h handler
  2026.                 mov     dx,offset OurInt16      ;DS:DX=new vector
  2027.                 int     21h                     ;let DOS set vector
  2028.  
  2029.                 mov     ax,2521h                ;to set our INT21h handler
  2030.                 mov     dx,offset OurInt21      ;DS:DX=new vector
  2031.                 int     21h                     ;let DOS set vector
  2032.  
  2033.                 mov     ax,2528h                ;to set our INT28h handler
  2034.                 mov     dx,offset OurInt28      ;DS:DX=new vector
  2035.                 int     21h                     ;let DOS set vector
  2036.  
  2037.                 mov     word ptr savearea+2, ds ;seg of scrnhold
  2038.                 mov     ax, offset scrnhold     ;ofs of scrnhold
  2039.                 mov     word ptr savearea, ax   ;for scrn-save
  2040.  
  2041.                 mov     ah, 15
  2042.                 int     10h                     ;get current video mode
  2043.  
  2044.                 cmp     al, 7                   ;if BW, then we need
  2045.                 jne     IsColor
  2046.                 mov     word ptr videocard + 2, 0B000h ;to set video address
  2047.                 mov     word ptr videocard, 0          ;to B000:0000
  2048.                 mov     videoflag, 1                   ;and remember mode 
  2049.                 jmp     SayHello
  2050. IsColor:
  2051.                 mov     word ptr videocard + 2, 0B800h ;if color mode, then
  2052.                 mov     word ptr videocard, 0          ;address is B800:0000
  2053.                 mov     videoflag, 0                   ;remember we're in color
  2054.  
  2055. ; ------------  DISLAY MSG ABOUT HOW WELL THIS IS ALL RUNNING
  2056. SayHello:
  2057.                 mov     dx,offset HotKeyMsg
  2058.                 mov     ah,9
  2059.                 int     21h                     ;disp hot key msg.
  2060.  
  2061.                 mov     dx,(TsrCurtain-ComEntry+100h+StackSize+15) SHR 4
  2062.                 mov     ax,3100h + xOk          ;TSR, AL=err level
  2063.                 int     21h
  2064. Init            endp
  2065.  
  2066. ;==============================================================================
  2067.  
  2068. Cseg            ends
  2069.                 end     ComEntry
  2070.  
  2071.  
  2072. ===============================================================
  2073.  
  2074. Cseg            ends
  2075.                 end     ComEntry
  2076.  
  2077.